home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-08-28 | 84.2 KB | 1,790 lines | [TEXT/MPS ] |
- /*
- File: TOCEnts.c
-
- Contains: Container Manager TOC Entry Routines
-
- Written by: Ira L. Ruben
-
- Owned by: Ed Lai
-
- Copyright: © 1991 - 1996 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- <3> 8/13/96 DM 1362809: disable containers on error
- <3> 7/28/95 EL #1272079: Value should not use the continue
- flag until being written to TOC.
- <2> 8/26/94 EL #1181622 Ownership update.
- <3> 6/3/94 EL Rename Values.h/c to ValueRtn.h/c.
- <2> 3/31/94 EL Export cmFreeProperties. #1150214
- <1> 2/3/94 EL first checked in
- <4> 2/2/94 EL Fix memory leak by freeing property and
- value associated with a reference.
- <3> 10/29/93 EL Add deleteAssocatedRef call which would
- delete a reference associated with a value
- without leaving it on the delete list
- <2> 10/21/93 EL ValueHdr should not point to deleted
- property
- To Do:
- */
-
- /*---------------------------------------------------------------------------*
- | |
- | <<< TOCEnts.c >>> |
- | |
- | Container Manager TOC Entry Routines |
- | |
- | Ira L. Ruben |
- | 12/02/91 |
- | |
- | Copyright Apple Computer, Inc. 1991-1992 |
- | All rights reserved. |
- | |
- *---------------------------------------------------------------------------*
-
- This file contains the manipulation routines to handle TOC objects below the object
- level. The objects and the means to get at them are maintained in TOCObjects.c. The
- split is done this way to keep the accessing of objects separate from the stuff
- represented by those objects.
-
- The following diagram shows the pointer relationships among the various data structures
- descending from an object. The data structurs leading to the object itself are handled
- independently by the TOCObjs.c routines. They are of no concern here as the structures
- described here are of no concern to TOCObjs.c !
-
- Arrows of the form "<---->" represent double links of forward/backward link lists. Arrows
- of the form "--->" are pointers in the indicated direction. A "+" in an arrow just means
- it's turning or intersecting since there is no other means to notate it given the
- character set used!
-
- A name above a box corresponds to the actual typedef struct name. A "*name*" just
- signifies what the structure is and destinguishes it from other similar structures in the
- diagram. Names in brackets are fields in the structures. The fields representing the
- list links and headers are not shown.
-
-
- +<-----+<--------------------+<--------------------+
- | | | |
- TOCObject | | TOCProperty | |
- *------------------*<--+ *--------------* *--------------* *--------------*
- | *Object* |<------->| *Property1* |<---->| *Property2* |<---->| *Property3* |
- | [objectID] | +>+---->| [propertyID] | | [propertyID] | | [propertyID] |
- | [container] | | | *--------------* *--------------*<-+ *--------------*<-+
- | [nextObject] | | | | |
- |[nextTypeProperty]| | | | |
- | [objectFlags] | | | +->... +->...
- *------------------* | |
- | | TOCValueHdr TOCValue
- | | *--------------* *----------*
- | +--->| *ValueHdr1* |<---->| *Value1* |
- The nextObject +<-----| [typeID] | | [flags] |
- links ALL objects. | | [container] | | [value] |
- | | [valueFlags] | *----------*
- | | [generation] | |
- | +--->| [size] | |
- The nextTypeProperty | | *--------------*<----------+
- is used for two | |
- separate chains; | |
- one that links all | |
- property objects, | | *--------------* *-----------* *-----------*
- and one that links | +--->| *ValueHdr2* |<---->| *Value21* |<---->| *Value22* |
- all type objects. +<-----| [typeID] | | [flags] | | [flags] |
- | | [container] | | [value] | | [value] |
- | | [valueFlags] | *-----------* *-----------*
- | | [generation] | | |
- | +--->| [size] | | |
- | | *--------------*<-----------+<-----------------+
- | | (continued)
- | |
- | |
- | |
- | | *--------------* *-----------* Note, all value
- | +--->| *ValueHdr3* |<---->| *Value3* | entries have a ptr
- +<-----| [typeID] | | [flags] | to their "owning"
- | [container] | | [value] | ValueHdr to be able
- | [valueFlags] | *-----------* to get at the TypeID
- | [generation] | | and container.
- | [size] | |
- *--------------*<-----------+
-
-
- The above diagram shows a single object with three properties. Properties are chained off
- the object. Only the layout for the first property is shown. Propert1 has three values.
- Values are chained off a value header and the value headers are chained off the properties.
- Value2 represents a continued value. They are chained together, and each value entry
- (continued or not) has a back pointer to its value header. Similarly, the value headers
- have back pointers to their property and the properties back to their object. This
- allows us to get at the owning container and list headers.
-
- Not shown in this example diagram is the handling of global names. They are contained in
- a binary tree symbol table. Each entry in that symbol table contains the symbol itself
- and a back pointer to its corresponding value entry for which the global name represents
- the value. The value, in turn, points to its global name symbol table entry. This
- global name layout allows us to keep the names in memory for mapping of global names
- to objects or vice versa. By keeping the global names on a binary tree we can
- efficiently walk the tree to write the value out to the container. This is more efficient
- than the alternative, which is walking the entire TOC structure looking only for the
- global name value pointers to just write them out.
-
- Note, the API permits deletion of objecs and values. This is implemented by two separate
- deletion lists. There is a list of all deleted TOCObject's and another list of all
- deleted TOCValueHdr's. Pointers to these are returned to the API user as "refNums". Thus
- these are the only entities that we must never free in an open container. All the other
- stuff, including TOCValue's off the TOCValueHdr's, can be freed when a delete is done.
- The reason we keep this stuff around at all is to protect ourselves against the silly
- user passing a "refNum" to a deleted object back to us! The "refNum"s remain valid
- pointers. When a TOCObject or TOCValueHdr is put on its list, it is flagged as deleted.
- Wenever a API caller passes in a "refNum" we check it to make sure it's still valid and
- yell if it isn't. It's a lot of machinery for this. But do you have a better idea?
-
-
- ---> Help for those looking at this file for the firtst time <---
-
- The place to start looking is at the four routines cmAppendValue(), defineValue(),
- defineProperty(), and cmDefineObject(). They are the main routines that implement the
- above structures. Understand them and the rest should fall out. That's how this file was
- developed and all other stuff "spun out" from there.
- */
-
-
- #include <stddef.h>
- #include <string.h>
- #include <setjmp.h>
- #include <stdio.h>
-
- #ifndef __CMTYPES__
- #include "CMTypes.h"
- #endif
- #ifndef __CM_API__
- #include "CMAPI.h"
- #endif
- #ifndef __LISTMGR__
- #include "ListMgr.h"
- #endif
- #ifndef __TOCENTRIES__
- #include "TOCEnts.h"
- #endif
- #ifndef __TOCOBJECTS__
- #include "TOCObjs.h"
- #endif
- #ifndef __TOCIO__
- #include "TOCIO.h"
- #endif
- #ifndef __GLOBALNAMES__
- #include "GlbNames.h"
- #endif
- #ifndef __CONTAINEROPS__
- #include "Containr.h"
- #endif
- #ifndef __VALUEROUTINES__
- #include "ValueRtn.h"
- #endif
- #ifndef __HANDLERS__
- #include "Handlers.h"
- #endif
- #ifndef __UPDATING__
- #include "Update.h"
- #endif
- #ifndef __DYNAMICVALUES__
- #include "DynValus.h"
- #endif
- #ifndef __BUFFEREDIO__
- #include "BufferIO.h"
- #endif
- #ifndef __REFERENCES__
- #include "Refs.h"
- #endif
- #ifndef __FREESPACE__
- #include "FreeSpce.h"
- #endif
- #ifndef __SESSIONDATA__
- #include "Session.h"
- #endif
- #ifndef __ERRORRPT__
- #include "ErrorRpt.h"
- #endif
- #ifndef __UTILITYROUTINES__
- #include "Utility.h"
- #endif
-
- CM_CFUNCTIONS
-
- /* The following generates a segment directive for Mac only due to 32K Jump Table */
- /* Limitations. If you don't know what I'm talking about don't worry about it. The */
- /* default is not to generate the pragma. Theoritically unknown pragmas in ANSI won't */
- /* choke compilers that don't recognize them. Besides why would they be looked at if */
- /* it's being conditionally skipped over anyway? I know, famous last words! */
-
- #if CM_MPW
- #pragma segment TOCEntries
- #endif
-
-
- #ifndef DEBUG_TOCENTRIES
- #define DEBUG_TOCENTRIES 0
- #endif
-
- #define dbgFile stdout /* trace display output file */
-
- /* The following is used in this file by cmWalkThroughEntireTOC(), etc. to hold the */
- /* function pointers, caller's refCon, and setjmp/longjmp escape vector. See that */
- /* routine for further details. */
-
- struct TOCActions { /* cmWalkThroughEntireTOC() communication area layout: */
- jmp_buf walkThroughEnv; /* MUST be 1st for AbortWalkThroughEntireTOC() macro */
- CMRefCon refCon; /* caller's refCon */
- TOCWalkReturns (*objectAction)(ContainerPtr container, TOCObjectPtr theObject, CMRefCon refCon);
- TOCWalkReturns (*propertyAction)(ContainerPtr container, TOCPropertyPtr theProperty, CMRefCon refCon);
- TOCWalkReturns (*valueHdrAction)(ContainerPtr container, TOCValueHdrPtr theValueHdr, CMRefCon refCon);
- TOCWalkReturns (*valueAction)(ContainerPtr container, TOCValuePtr theValue, CMRefCon refCon);
- };
- typedef struct TOCActions TOCActions, *TOCActionsPtr;
-
- static void deleteProperties(TOCObjectPtr theObject, CMRefCon refCon);
-
- /*---------------------------------------------------------------------------*
- | deleteAssociatedRef - remove the reference associated with this reference |
- *---------------------------------------------------------------------------*
-
- This routine checks to see if there is any reference assoicated with a value, if there
- is one, we also delete the reference.
-
- Since a reference is delete as a side effect, if the delete reference remains in the
- delete list and generate a update delete, then when the update is play back, the
- reference is deleted twice, once as the side effect and once as directed by the updating
- instruction. So we want to make sure the reference is completely gone so it would not
- be used to generate a delete update later.
- */
-
- static void CM_NEAR deleteAssociatedRef(ContainerPtr container, TOCValueHdrPtr theValueHdr)
- {
- TOCObjectPtr theObject;
- void *toc;
- CMBoolean orgSetting;
-
- if (HasRefDataObject(theValueHdr)) { /* delete refs object... */
- theObject = RefDataObject(theValueHdr);
- toc = container->toc;
- theObject->objectFlags &= ~ProtectedObject;
- cmUnlinkObject(toc, theObject); /* must do this first */
- orgSetting = cmKeepDeletedRefNums(toc);
- /* since the reference is deleted as a side effect, we don't want it to show up in */
- /* any delete list or touch list, so we going to free it directly, and not put it */
- /* on the delete or touch list. We do this by setting RefNumHandling to false. */
- cmChangeRefNumHandling(toc, false); /* allows freeing */
- cmDelete1Object(toc, theObject->objectID, (void *)container, deleteProperties);
- cmChangeRefNumHandling(toc, orgSetting); /* restore original value */
- }
-
- #if CMSHADOW_LIST
- if (HasRefShadowList(theValueHdr)) /* delete refs shadow list*/
- cmDeleteRefDataShadowList(theValueHdr);
- #endif
- }
-
- /*--------------------------------------------------------------*
- | cmMarkValueDeleted - remove a value for an object's property |
- *--------------------------------------------------------------*
-
- This routine takes a pointer to a value header in the specified container and deletes it.
- If the value was the only one for its property, the property is also deleted.
-
- Note, properties and values actually have their memory freed up. The value headers are
- placed on the deletedValues list to keep them around, but out of the TOC. This is to
- prevent dangling references from API user "refNum"s, because it is value header pointers
- that we give to the user as those "refNum"s. Such value headers are marked as "deleted"
- to prevent future use by the user.
-
- The moving of the deleted value headers to the deletedValues list can be suppressed by
- passing the deleteHdr parameter as true. Its memory is simply freed along with the rest.
- This is used currently in only one place for error recovery when dynamic value setup
- fails. There we want to free all the dynamic value layers (which are headers), and
- finally the base "real" value itself. It is that final value that is passed to here to
- get rid of.
- */
-
- void cmMarkValueDeleted(ContainerPtr container, TOCValueHdrPtr theValueHdr,
- CMBoolean deleteHdr)
- {
- TOCValuePtr nextValue, theValue;
- TOCPropertyPtr theProperty;
-
- /* Free all values for the value header... */
-
- theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
- while (theValue) {
- nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
- cmAddToFreeList(container, theValue, 0, 0); /* add space to free list */
- if (theValue->flags & kCMGlobalName) /* mark global name as deleted*/
- if (theValue->value.globalName.globalNameSymbol)
- MarkGlobalNameDeleted(theValue->value.globalName.globalNameSymbol);
- CMfree(theValue);
- theValue = nextValue;
- } /* value */
-
- /* Mark the value header as deleted and put it on the separate chain of deleted */
- /* values. Also keep track of total space deleted. */
-
- #if 0 /* now done in cmAddToFreeList() */
- container->spaceDeletedValue->value.imm.ulongValue += theValueHdr->size;
- #endif
-
- theProperty = theValueHdr->theProperty; /* prepare to remove hdr */
- cmDeleteListCell(&theProperty->valueHdrList, theValueHdr); /* del. valueHdr off list */
-
- deleteAssociatedRef(container, theValueHdr); /* delete any reference */
-
- if (deleteHdr || !cmKeepDeletedRefNums(container->toc)) { /* free the refNum? */
- CMfree(theValueHdr); /* this caller is serious!*/
- theValueHdr = NULL; /* so we know it's gone */
- } else { /* this is normal case... */
- cmInitList(&theValueHdr->valueList); /* no more values */
- theValueHdr->valueFlags |= ValueDeleted; /* mark hdr as deleted */
- cmAppendListCell(&container->deletedValues, theValueHdr); /* move to deletedValues */
- }
-
- /* If the value is the only one for a property, the property itself is deleted... */
-
- if (cmIsEmptyList(&theProperty->valueHdrList)) {
- CMfree(cmDeleteListCell(&theProperty->theObject->propertyList, theProperty));
- if (theValueHdr)
- theValueHdr->theProperty = NULL; /* so we won't access it */
- }
- }
-
-
- /*-----------------------------------------------------------------------*
- | cmDeleteProperty - remove a property and all its values for an object |
- *-----------------------------------------------------------------------*
-
- This routine takes a pointer to a property in the specified container and deletes it and
- all its values.
-
- Note, properties and values actually have their memory freed up. The value headers are
- placed on the deletedValues list to keep them around, but out of the TOC. This is to
- prevent dangling references from API user "refNum"s, because it is value header pointers
- that we give to the user as those "refNum"s. Such value headers are marked as "deleted"
- to prevent future use by the user.
-
- This routine differs from deleteProperties() below by the fact that this is an external
- routine for deleting one property for an object. It is used as an isolated and explicit
- delete of a property. deleteProperties(), on the other hand, is for deleting all the
- properties of an object when we know we're deleting the object itself. It is the action
- routine for the isolated delete of an object.
- */
-
- void cmDeleteProperty(ContainerPtr container, TOCPropertyPtr theProperty)
- {
- TOCValueHdrPtr theValueHdr, nextValueHdr;
- TOCValuePtr theValue, nextValue;
- TOCObjectPtr theObject;
-
- /* Mark all the values for the property as deleted... */
-
- theObject = theProperty->theObject;
- theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
-
- while (theValueHdr) {
- nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
-
- theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
- while (theValue) {
- nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
- cmAddToFreeList(container, theValue, 0, 0); /* add space to free list */
- if (theValue->flags & kCMGlobalName) /* mark global name as deleted*/
- if (theValue->value.globalName.globalNameSymbol)
- MarkGlobalNameDeleted(theValue->value.globalName.globalNameSymbol);
- CMfree(theValue);
- theValue = nextValue;
- } /* value */
-
- #if 0 /* now done in cmAddToFreeList() */
- container->spaceDeletedValue->value.imm.ulongValue += theValueHdr->size;
- #endif
-
- deleteAssociatedRef(container, theValueHdr); /* delete any reference */
-
- if (cmKeepDeletedRefNums(container->toc)) { /* keep refNums ? */
- cmInitList(&theValueHdr->valueList); /* no more values */
- theValueHdr->valueFlags |= ValueDeleted; /* mark valueHdr deleted */
- cmAppendListCell(&container->deletedValues,theValueHdr);/* move to deletedValues */
- cmImplicitDeleteValueTouch(theValueHdr, theObject); /* check touched updates */
- } else /* don't keep refNums */
- CMfree(theValueHdr);
-
- theValueHdr = nextValueHdr; /* around and around we go... */
- } /* valueHdr */
-
- /* Free the property itself since it now has no values... */
-
- CMfree(cmDeleteListCell(&theObject->propertyList, theProperty));
- }
-
-
- /*---------------------------------------------------------------------------------*
- | deleteProperties - delete ALL the properties and and their values for an object |
- *---------------------------------------------------------------------------------*
-
- This routine is an action routine for cmDelete1Object() initiated by cmMarkObjectDeleted()
- below to free up all the memory for values and properties for an object. The value
- headers are marked as deleted and move to the deletedValues list. This makes sure there
- are no dangling value "refCon"s (which are value header pointers) laying around. Such
- value headers are flagged as deleted to prevent their use (assuming the checks are put
- in, of course).
-
- Note, this routine differs from cmDeleteProperty() in that here we delete ALL the
- properties for an object. Not just a single property. Also, unlike cmDeleteProperty(),
- we know we are being called because an object is being deleted. We thus do no try to
- delete the object because all its properties are deleted as cmDeleteProperty() does.
-
- Also note, this "static" is intentionally left to default memory model under DOS since it
- is passed as a function pointer to cmDelete1Object().
- */
-
- static void deleteProperties(TOCObjectPtr theObject, CMRefCon refCon)
- {
- ContainerPtr container = (ContainerPtr)refCon;
- TOCPropertyPtr theProperty, nextProperty;
- TOCValueHdrPtr theValueHdr, nextValueHdr;
- TOCValuePtr theValue, nextValue;
-
- /* Free all the properties for this object... */
-
- theProperty = (TOCPropertyPtr)cmGetListHead(&theObject->propertyList);
- while (theProperty) {
- nextProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
-
- /* Mark all the value headers for the property as deleted and place them on a */
- /* separate list. The values themselves are freed. */
-
- theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
- while (theValueHdr) {
- nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
-
- theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
- while (theValue) {
- nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
- cmAddToFreeList(container, theValue, 0, 0); /* add space to free list */
- if (theValue->flags & kCMGlobalName) /* mark global name as deleted*/
- if (theValue->value.globalName.globalNameSymbol)
- MarkGlobalNameDeleted(theValue->value.globalName.globalNameSymbol);
- CMfree(theValue);
- theValue = nextValue;
- } /* value */
-
- /*CMfree(theValueHdr);*/ /* no longer freed */
-
- #if 0 /* now done in cmAddToFreeList() */
- container->spaceDeletedValue->value.imm.ulongValue += theValueHdr->size;
- #endif
-
- deleteAssociatedRef(container, theValueHdr); /* delete any reference */
-
- if (cmKeepDeletedRefNums(container->toc)) { /* keep refNums ? */
- cmInitList(&theValueHdr->valueList); /* no more values */
- theValueHdr->valueFlags |= ValueDeleted; /* mark valueHdr deleted*/
- cmAppendListCell(&container->deletedValues,theValueHdr);/* move to deletedValues*/
- cmImplicitDeleteValueTouch(theValueHdr, theObject); /* check touched updates*/
- } else /* don't keep refNums */
- CMfree(theValueHdr);
-
- theValueHdr = nextValueHdr; /* around and around we go... */
- } /* valueHdr */
-
- CMfree(theProperty); /* property not needed */
- theProperty = nextProperty;
- } /* property */
-
- cmInitList(&theObject->propertyList); /* no properties for object */
- }
-
-
- /*-------------------------------------------------*
- | cmMarkObjectDeleted - mark an object as deleted |
- *-------------------------------------------------*
-
- This routine is called to remove an entire object from the TOC for the specified
- container. All its associated structures (properties and values) are made unavailable
- from the TOC. The space for the object is NOT freed. The object is placed on a list of
- deleted objects and marked as deleted. By doing it this way, any outstanding "refNum"s
- we gave the API user (which are pointers to this stuff) will still be "valid" (sort of
- -- at least not looking at garbage).
-
- Moving the object structure takes all its substructures (properties, value headers, and
- values with it). But the API user may also have references to value headers! So we have
- to mark these as deleted as well (we can still, however, free the memory for the value
- lists attached to the value headers and the properties which contain the value headers).
-
- The object deletion (and actual marking) is handled by cmDelete1Object(). It takes an
- action routine to walk an object's properties. We pass it deleteProperties() to do that.
- It is there that we delete all the properties and values and move the value headers to
- there deletedValues list.
- */
-
- void cmMarkObjectDeleted(ContainerPtr container, TOCObjectPtr theObject)
- {
- /* Here we use cmDelete1Object() which removes an object by object id. It takes a */
- /* function pointer (markValuesDeleted here) to delete the stuff attached to the */
- /* object prior to deleting the object itself. Note, we must also remember to unlink */
- /* the object from the master chains. We do this first since cmDelete1Object() will */
- /* use the same link fields to relink the object to the list of deleted objects. */
-
- cmUnlinkObject(container->toc, theObject); /* must do this first */
- cmDelete1Object(container->toc, theObject->objectID, (void *)container, deleteProperties);
- }
-
-
- /*-------------------------------------------------------------------------------*
- | cmFreeProperties - free ALL the properties and and their values for an object |
- *-------------------------------------------------------------------------------*
-
- This routine is called to unconditionally free all the properties for an
- objects and the values and other data structures associated with those properties. It is
- done when an TOC is deleted, i.e, for cmFreeTOC().
-
- Note, here we can just free the properties and associated data structures. We don't have
- to respect the fact that they are on a list whose header belongs to the the object. That
- is because we know we are going to delete the object too.
-
- This routine differs from deleteProperties(), and cmDeleteProperty() in that we just free
- the data. There is no list maintenance and we do not have to worry about moving deleted
- objects or value headers to their delete lists.
-
- Note, this "static" is intentionally left to default memory model under DOS since it is
- passed as a function pointer to cmDestroyTOC().
- */
-
- void cmFreeProperties(TOCObjectPtr theObject, CMRefCon refCon)
- {
- ContainerPtr container = (ContainerPtr)refCon;
- TOCPropertyPtr theProperty, nextProperty, theDynProperty = NULL;
- TOCValueHdrPtr theValueHdr, nextValueHdr;
- TOCValuePtr theValue, nextValue;
-
- /* Free this object's touched list (if any)... */
-
- cmDeleteTouchedList(theObject, container);
-
- /* Free all the properties for this object... */
-
- theProperty = (TOCPropertyPtr)cmGetListHead(&theObject->propertyList);
- while (theProperty) {
- nextProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
-
- /* Free all the values and their headers for a property. If a "real" value is for a */
- /* dynamic value, the associated dynamic value layers are also freed. This should */
- /* end up freeing ALL the dynamic values for this object, because we visit all the */
- /* real values in the object. This will leave the dynamic value property with no */
- /* dynamic values on it. But we have to be careful to not free the dynamic value */
- /* property until we have looked at all the values for the object. Thus we remember*/
- /* where the property is and delete it last. */
-
- if (theProperty->propertyID == CM_StdObjID_DynamicValues) { /* skip dyn value prop. */
- theDynProperty = theProperty; /* but remember it */
- theProperty = nextProperty;
- continue;
- }
-
- theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
-
- if (theValueHdr->dynValueData.dynValue != NULL) /* free dynamic values... */
- cmDeleteAllDynamicValueLayers(theValueHdr->dynValueData.dynValue, false);
-
- while (theValueHdr) {
- nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
-
- theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
- while (theValue) {
- nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
-
- #if 0 /* table already deleted! */
- if (theValue->flags & kCMGlobalName)
- if (theValue->value.globalName.globalNameSymbol)
- MarkGlobalNameDeleted(theValue->value.globalName.globalNameSymbol);
- #endif
-
- CMfree(theValue);
- theValue = nextValue;
- } /* value */
-
- #if CMSHADOW_LIST
- if (HasRefShadowList(theValueHdr)) /* delete refs shadow list */
- cmDeleteRefDataShadowList(theValueHdr);
- #endif
-
- CMfree(theValueHdr);
- theValueHdr = nextValueHdr;
- } /* valueHdr */
-
- CMfree(theProperty);
- theProperty = nextProperty;
- } /* property */
-
- /* If there was a dynamic value property, then by this time all of its dynamic values */
- /* have been deleted. All that's left is to remove the property from the object. But */
- /* given that we're expected to MAKE SURE all TOC memory is released we're going to be*/
- /* paranoid here and formally attempt to free any value headers and their value */
- /* segments just as we did above. There shouldn't be any, but lets just be sure. */
-
- if (theDynProperty != NULL) {
- theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theDynProperty->valueHdrList);
- while (theValueHdr) {
- nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
- theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
- while (theValue) {
- nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
- CMfree(theValue);
- theValue = nextValue;
- } /* value */
- CMfree(theValueHdr);
- theValueHdr = nextValueHdr;
- } /* valueHdr */
- } /* theDynProperty */
- }
-
-
- /*----------------------------------------------*
- | cmFreeTOC - free all the structures in a TOC |
- *----------------------------------------------*
-
- This routine is called to free all the structures in a TOC. For updating, a container may
- have its own TOC and the target TOC. Thus the TOC pointer is an explicit parameter. It
- is passed as a pointer to the toc pointer because freeing the TOC will NULL out the
- caller's pointer.
-
- All memory for objects, properties, and types is freed. On return the toc pointer in the
- container is NULL as well as all the master link head/tail pointers.
- */
-
- void cmFreeTOC(ContainerPtr container, void **toc)
- {
- TOCValueHdrPtr theValueHdr, nextValueHdr;
- TOCValuePtr theValue, nextValue;
-
- /* Free the main data structures... */
-
- cmDestroyTOC(toc, (void *)container, true, cmFreeProperties);
-
- /* Free all the value headers on the deletedValues list. This list results in deletes */
- /* of objects, properties, or value headers themselves. Objects are similarly put */
- /* on a separate list of deleted objects. This list is freed by cmDestroyTOC(). */
-
- theValueHdr = (TOCValueHdrPtr)cmGetListHead(&container->deletedValues);
- while (theValueHdr) {
- nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
- theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
- while (theValue) {
- nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
- CMfree(theValue);
- theValue = nextValue;
- } /* value */
- CMfree(theValueHdr);
- theValueHdr = nextValueHdr;
- } /* valueHdr */
-
- cmInitList(&container->deletedValues);
- }
-
-
- /*-------------------------------*
- | cmSetValueBytes - set a value |
- *-------------------------------*
-
- This routine is used to just set the value (theValueBytes) with the specified data. The
- data is placed in theValueBytes as a function of the specified type, dataType. There
- are 7 types:
-
- (1). Value_NotImm This is the "normal" case for a non-immediate value data (a
- container offset) and data length. The value/valueLen (i.e.,
- notImm) variant of a TOCValueBytes is used.
-
- (2). Value_Imm_Chars All of these use the corresponding imm variant of the ptr to the
- (3). Value_Imm_Long TOCValueBytes struct passed here. The data is placed in the
- (4). Value_Imm_Short field according to type and, for the hell of it, the notImm
- (5). Value_Imm_Byte valueLen field set accordingly. Note signed and unsigned are
- (6). Value_Imm_Ptr treated alike.
-
- (7). Value_GlobalName This is a special case for global name strings. The data is
- assumed to be a pointer to a global name string. A global name
- symbol table entry is created (using cmDefineGlobalName()) with
- the string. The pointer to the symbol table entry is set as
- the value data. Note, a global name symbol table entry also
- has a back pointer to its "owning" TOCValue. THE CALLER MUST
- SET THIS FIELD! Here we only deal with the value bytes.
-
- The function returns the input theValueBytes pointer as its result. NULL is returned
- for Value_GlobalName if the allocation fails and an error will have been reported (note,
- as usual we're covering our ass again here).
- */
-
- TOCValueBytesPtr cmSetValueBytes(const ContainerPtr container,
- TOCValueBytesPtr theValueBytes,
- ConstValueType dataType, CM_ULONG data,
- CM_ULONG dataLength)
- {
- GlobalNamePtr g;
- CMBoolean dup;
-
- switch (dataType) { /* set 'em... */
- case Value_NotImm: theValueBytes->notImm.value = (CM_ULONG)data;
- theValueBytes->notImm.valueLen = (CM_ULONG)dataLength;
- break;
-
- case Value_Imm_Chars: if (dataLength > sizeof(CM_ULONG)) dataLength = sizeof(CM_ULONG);
- theValueBytes->imm.ulongValue = 0; /* zero extra bytes */
- memcpy((CM_CHAR *)theValueBytes->imm.ucharsValue, (CM_CHAR *)data, (size_t)dataLength);
- theValueBytes->notImm.valueLen = dataLength;
- break;
-
- case Value_Imm_Long: theValueBytes->imm.ulongValue = (CM_ULONG)data;
- theValueBytes->notImm.valueLen = (CM_ULONG)sizeof(CM_ULONG);
- break;
-
- case Value_Imm_Short: theValueBytes->imm.ulongValue = 0; /* zero extra bytes */
- theValueBytes->imm.ushortValue = (CM_USHORT)data;
- theValueBytes->notImm.valueLen = (CM_ULONG)sizeof(CM_USHORT);
- break;
-
- case Value_Imm_Byte: theValueBytes->imm.ulongValue = 0; /* zero extra bytes */
- theValueBytes->imm.ubyteValue = (CM_UCHAR)data;
- theValueBytes->notImm.valueLen = (CM_ULONG)sizeof(CM_UCHAR);
- break;
-
- case Value_GlobalName: g = cmCreateGlobalName(container->globalNameTable, (CM_UCHAR *)data, &dup);
- if (g == NULL) {
- Container_Disable(container);
- ERROR2(CM_err_NoGlobalName, data, CONTAINERNAME);
- theValueBytes = NULL;
- } else if (dup) {
- Container_Disable(container);
- ERROR2(CM_err_DupGlobalName, data, CONTAINERNAME);
- theValueBytes = NULL;
- } else {
- theValueBytes->globalName.globalNameSymbol = g;
- theValueBytes->globalName.offset = 0;
- }
- break;
- } /* switch */
-
- return (theValueBytes);
- }
-
-
- /*------------------------------------------------------------------------*
- | cmSetTOCValueHdrFlags - echo a value's flags in its value header flags |
- *------------------------------------------------------------------------*
-
- This routine is used to echo the settings of a value's flags in the value header which
- "owns" the value (TOCValue's are on a list whose header is in a TOCValueHdr). This is
- done because a CMValue "refNum" that an API user is given and in turn given back to us
- is a pointer to a TOCValueHdr. It is sometimes more convenient therefore to check the
- kind of value we have by looking at the header then "going out" to the value. In all but
- continued values there is only one TOCValue entry on the valueList anyway. So echoing
- is more efficient than always going after the tail or head (they're the same) of a
- valueList just to see the flags and the kind of value.
-
- The function updates the valueFlags field in the passed TOCValueHdr according to the
- value flags also passed. The updated valueFlags are also returned as the function
- result.
- */
-
- CM_USHORT cmSetTOCValueHdrFlags(TOCValueHdrPtr theValueHdr, const CM_USHORT flags)
- {
- CM_USHORT valueFlags = (CM_USHORT)(theValueHdr->valueFlags & ValueCstFlags);
- ContainerPtr container;
- TOCValuePtr the1stValue;
- CM_CHAR flagsStr[15];
-
- /* The only non-obvious thing here is that we must take special precautions for */
- /* continued values. Since the last continued value flags does NOT say kCMContinued, */
- /* we check the first value of the given value header which must say it is continued. */
- /* From that we can reliably set the value header flag to say its value list is */
- /* continued. */
-
- if (flags & kCMGlobalName) /* global names */
- valueFlags |= (ValueGlobal | ValueDefined);
- else if (flags & kCMImmediate) /* immediates */
- if (flags & kCMContinued)
- valueFlags |= (ValueImmediate | ValueContinued);
- else
- valueFlags |= (ValueImmediate | ValueDefined);
- else if (flags & kCMContinued) /* continued */
- valueFlags |= ValueContinued;
- else if (flags & kCMDynValue)
- valueFlags |= ValueDynamic;
- else if (flags == 0) /* garden-variety value (...maybe) */
- if ((the1stValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList)) != NULL &&
- (the1stValue->flags & kCMContinued) != 0)
- valueFlags |= (ValueContinued | ValueDefined);
- else
- valueFlags |= ValueDefined;
- else { /* huh? */
- container = theValueHdr->container;
- Container_Disable(container);
- ERROR1(CM_err_Internal1, cmltostr(flags, -4, true, flagsStr));
- valueFlags = theValueHdr->valueFlags;
- }
-
- return (theValueHdr->valueFlags = valueFlags);
- }
-
-
- /*---------------------------------------------------*
- | cmCreateValueSegment - create a new value segment |
- *---------------------------------------------------*
-
- This routine is called to create a new value segment and to fill in its fields with the
- passed parameters. A pointer to the newly created value segment is returned. NULL is
- returned if there is an allocation error.
-
- Segments are created generally created for continued values and appended on to the value
- chain whose header is container in a value header. However, due to such things like
- value inserts a value segment may not necessarily be appended to the end of its chain.
- Thus this routine, which brings a value segment into existence, is a separate routine.
-
- Note, only the segment is created here. It is up to the caller to chain it to its value
- header and echo the flags in that header.
- */
-
- TOCValuePtr cmCreateValueSegment(TOCValueHdrPtr theValueHdr, TOCValueBytesPtr value,
- const CM_USHORT flags)
- {
- ContainerPtr container = theValueHdr->container;
- TOCValuePtr theValue;
-
- if ((theValue = (TOCValuePtr)CMmalloc(sizeof(TOCValue))) == NULL) { /* create seg... */
- Container_Disable(container);
- ERROR1(CM_err_NoValueEntry, CONTAINERNAME);
- return (NULL);
- }
-
- theValue->theValueHdr = theValueHdr; /* fill in the fields... */
- #if CMKEEP_CONTINUE_FLAG
- theValue->flags = flags;
- #else
- theValue->flags = flags & ~kCMContinued;
- #endif
- theValue->value = *value;
- cmNullListLinks(theValue);
-
- theValue->container = container->updatingContainer; /* who "owns" this new value */
- theValue->logicalOffset = 0; /* filled in elsewhere */
-
- return (theValue);
- }
-
-
- /*----------------------------------------------------------*
- | cmAppendValue - append a value segment to a value header |
- *----------------------------------------------------------*
-
- This routine takes a pointer to a value definition (the actual bytes) and its flags
- and creates a TOCValue struct which is appended to the specified value header. The
- function returns the value pointer as its result (i.e. pointer to the newly created
- value). NULL is returned and an error reported if the allocation of the TOCValue fails.
-
- This routine is used to append actual values to an existing value header. It is allowed
- to call cmDefineObject() with a NULL value bytes pointer. In that case an object is
- created with a value header but no value sublist off of that header. This routine can
- be called to build that sublist.
-
- Internally, cmDefineObject() itself will result in a call to here to add the first value
- if one is so passed to it.
- */
-
- TOCValuePtr cmAppendValue(TOCValueHdrPtr theValueHdr, TOCValueBytesPtr value,
- const CM_USHORT flags)
- {
- ContainerPtr container = theValueHdr->container;
- TOCValuePtr theValue;
- CMObjectID propertyID;
- CM_CHAR offsetStr[15], lenStr[15];
-
- /* Check to see that the offset in the value is in range of the container. We only do */
- /* while we're loading a container (i.e., the container is not considered in a valid */
- /* state yet. */
-
- if (!container->tocFullyReadIn && (flags & kCMImmediate) == 0 &&
- (value->notImm.value > container->maxValueOffset ||
- value->notImm.value + value->notImm.valueLen > container->maxValueOffset)) {
- Container_Disable(container);
- ERROR3(CM_err_BadOffset, cmltostr(value->notImm.value, 1, false, offsetStr),
- cmltostr(value->notImm.valueLen, 1, false, lenStr), CONTAINERNAME);
- return (NULL);
- }
-
- /* Create the value segment, fill in its fields, and add it to its value hdr list... */
-
- theValue = cmCreateValueSegment(theValueHdr, value, flags);
- if (theValue == NULL) return (NULL);
- cmAppendListCell(&theValueHdr->valueList, theValue);
-
- /* By saving the current total value size in the value segment we get the logical */
- /* offset of this segment. For only a single segment this will always be 0. But for */
- /* continued values, each additional segment will then get its starting logical */
- /* offset. By remembering the original offset of each segment we will be able to */
- /* "do the math" to determine inserts and deletes of this data should be edited (we */
- /* don't care about that here). */
-
- theValue->logicalOffset = theValueHdr->size; /* set segment's logical offset */
-
- /* Echo the flags and sum total size in the value header for this value entry... */
-
- (void)cmSetTOCValueHdrFlags(theValueHdr, flags); /* echo flags in the value hdr */
-
- if ((theValueHdr->valueFlags & ValueGlobal) != 0) /* global name */
- theValueHdr->size += GetGlobalNameLength(theValue->value.globalName.globalNameSymbol) + 1;
- else if (theValue->flags & kCMImmediate)
- theValueHdr->size += theValue->value.notImm.valueLen; /* immediate byte, word, long */
- else
- theValueHdr->size += theValue->value.notImm.valueLen; /* non-immediate data */
-
- /* If we're currently reading in the TOC (indicated by tocFullyReadIn == false), then */
- /* we must set the logical offset of each value segment. This is the user's "view" of*/
- /* the record. We use it only for updating to determine insert/delete operations for */
- /* creating update information. */
-
- /* Also during this time, we must build the global name table for values that */
- /* correspond to global names. This is an in-memory table of the global names to make*/
- /* looking those things up more efficient. */
-
- if (!container->tocFullyReadIn) { /* set logicalSize while loading */
- theValueHdr->logicalSize = theValueHdr->size; /* (only used for updating) */
-
- propertyID = theValueHdr->theProperty->propertyID;/* see if we have a global name...*/
- if (propertyID == CM_StdObjID_GlobalTypeName ||
- propertyID == CM_StdObjID_GlobalPropName)
- if (!cmBuildGlobalNameTable(theValue)) { /* ...build (up) global name table*/
- CMfree(theValue);
- return (NULL);
- }
- }
-
- return (theValue); /* give caller back the value ptr */
- }
-
-
- /*--------------------------------------------*
- | defineValue - define a value for an object |
- *--------------------------------------------*
-
- This routine is used only by defineProperty() to create a value header and value for a
- property (theProperty) associated with an object. A value is chained to its value
- header and the value headers to their property.
-
- The value's typeID, value, generation and flags are given. The function returns a pointer
- to the value's value header if the value was successfully created and NULL if it wasn't.
- An error is reported if NULL is returned. Note, while error calls are not supposed to
- return we assume here they due just to be safe!
-
- The flags indicate the kind of value we have. Continued values are special, in that any
- continued value after the first for the current value header (the most recent one
- created) do not cause a new value header to be created for the property. The returned
- value header is not linked into its property. The caller, i.e., defineProperty() is
- responsible for that. It has to know whether a continued value caused a new value header
- to be created. Thus the newValueHdr switch is set true if it was and false if it is
- just another continued value.
-
- Note, the pointer to the value bytes may be NULL. This indicates that only the value
- header for a yet to be defined value is to be created.
- */
-
- static TOCValueHdrPtr CM_NEAR defineValue(ContainerPtr container,
- TOCPropertyPtr theProperty,
- const CM_ULONG typeID,
- TOCValueBytesPtr value,
- const CM_ULONG generation,
- const CM_USHORT flags,
- CMBoolean *newValueHdr)
- {
- TOCValueHdrPtr theValueHdr;
-
- /* If the last value for the property is for a continued value, then the new value is */
- /* also considered continued (whether the flags say so or not -- the last continued */
- /* value doesn't have its contined bit set). Continued values must all have the same */
- /* type ID. Note, that since the last value of a set of continued set of values does */
- /* not have the continued flag, but all of them cause the continued flag in the value */
- /* header to be set, we need to know if the previous continued value was the last one.*/
- /* The ValueDefined bit in the header is used to do this (it's why it was created). If*/
- /* the last value was continued but is fully defined (because the last value was the */
- /* end of the continuation -- its flags were 0), then we must have a NEW incoming */
- /* value. Having said all this, it should be noted that all this continue crap is */
- /* used ONLY when reading in a TOC from the container. At all other times continued */
- /* values are "manufactured" directly by CMWriteValueData(). */
-
- /* If we don't have a continued value, we create a new value header for the property. */
- /* In all cases (continued and not continued) we, of course, create a new value entry.*/
- /* The only difference is weather the value header is a new one or a previous one. */
-
- if ((theValueHdr = (TOCValueHdrPtr)cmGetListTail(&theProperty->valueHdrList)) != NULL &&
- (theValueHdr->valueFlags & ValueContinued) != 0 &&
- (theValueHdr->valueFlags & ValueDefined) == 0){ /* most recent value is cont'd... */
- if (theValueHdr->typeID != typeID) { /* ...types must agree */
- Container_Disable(container);
- ERROR1(CM_err_BadContinue, CONTAINERNAME);
- return (NULL);
- }
- *newValueHdr = false; /* ...use most recent value hdr */
- } else { /* create a new value header... */
- if (container->tocFullyReadIn) /* if valid TOC, check for dups...*/
- if (cmGetPropertyType(theProperty, typeID) != NULL) {
- Container_Disable(container);
- ERROR2(CM_err_DupType, cmGetGlobalTypeName(container, typeID), CONTAINERNAME);
- return (NULL);
- }
-
- if ((theValueHdr = (TOCValueHdrPtr)CMmalloc(sizeof(TOCValueHdr))) == NULL) {
- Container_Disable(container);
- ERROR1(CM_err_NoValueEntry, CONTAINERNAME);
- return (NULL);
- }
-
- *newValueHdr = true;
- cmNullListLinks(theValueHdr); /* init value hdr fields... */
- cmInitList(&theValueHdr->valueList); /* ...hdr is linked by caller */
- theValueHdr->valueFlags = 0; /* ...no flags yet */
- theValueHdr->typeID = typeID; /* ...use passed type ID */
- theValueHdr->container = container; /* ...ptr to owning container */
- theValueHdr->theProperty = theProperty; /* ...ptr to owning property */
- theValueHdr->size = 0; /* ...total value size initially 0*/
- theValueHdr->logicalSize = 0; /* ...same for logicalSize */
- theValueHdr->generation = generation; /* ...generation number */
- theValueHdr->useCount = 0; /* ...no uses yet (well, not here)*/
- theValueHdr->valueRefCon = NULL; /* ...no refCon value yet */
- RefDataObject(theValueHdr) = NULL; /* ...no value so no references */
- theValueHdr->touch = NULL; /* ...not interested in touch here*/
- DYNEXTENSIONS(theValueHdr) = NULL; /* ...no dynamic value extensions */
-
- /* Note: Dynamic value extensions are ONLY set by cmNewDynamicValue(). */
- /* References value extensions are ONLY set by ?(). */
- }
-
- /* Now we can create a new value entry. To make it easire to check for the various */
- /* attributes of values we set the valueFlags in the value header according to the */
- /* value flags themselves. We only do it now if there currently is no value (see */
- /* below for more about that). If there is a value, we will be calling cmAppendValue()*/
- /* which does the same thing. There is no sense doing it twice. */
-
- /* We also allow the pointer to the value bytes to be NULL. This is defined as the */
- /* creation of a object with a valueHdr but with NO VALUE. This occurs when the API */
- /* caller needs only a "refNum" to a value. We use value header pointers as such */
- /* "refNum"s. Other API calls are done which specify the same object with a real */
- /* value to be attached to the object. Values are then attached to the valueHdr, BUT */
- /* NOT FROM HERE! This routine is responsible only for creating objects WITH values. */
- /* If the value creation is suppressed that is not this routines problem! Take it */
- /* somewhere else! */
-
- if (value == NULL) /* append new value if we have one*/
- (void)cmSetTOCValueHdrFlags(theValueHdr, flags);
- else if (cmAppendValue(theValueHdr, value, flags) == NULL)
- return (NULL);
-
- return (theValueHdr); /* give caller the value hdr ptr */
- }
-
-
- /*----------------------------------------------------------------*
- | defineProperty - define a property and its value for an object |
- *----------------------------------------------------------------*
-
- This routine is used only by cmDefineObject() to create a property and its associated
- values for an object. The properties are chained to the object, values to value headers,
- and the value headers to the properties.
-
- The value's typeID, value, generation and flags are given. The function returns a pointer
- to the property if the property was successfully created and NULL if it wasn't. An error
- is reported if NULL is returned. Note, while error calls are not supposed to return we
- assume here they due just to be safe!
-
- A new property for the object is only created if it has a different property id. The
- value is chained to the property (a previous one or the new one for a new id). The
- returned property pointer is not linked to the object. The caller, i.e., cmDefineObject()
- is responsible for that. It has to know whether a new property was created. Thus the
- newProperty switch is set true if it was and false if it is just a previously used
- property for the object.
-
- Note, the value associated with the property is appended to the the end of the property's
- value list. The caller has the option of getting the pointer to the value header
- (theValueHdr) when theValueHdr is not passed as NULL.
- */
-
- static TOCPropertyPtr CM_NEAR defineProperty(ContainerPtr container,
- TOCObjectPtr theObject,
- const CM_ULONG propertyID,
- const CM_ULONG typeID,
- TOCValueBytesPtr value,
- const CM_ULONG generation,
- const CM_USHORT flags, CMBoolean *newProperty,
- TOCValueHdrPtr *theValueHdr)
- {
- TOCPropertyPtr theProperty;
- CMBoolean newValueHdr;
-
- /* Scan all the properties for the object to see if we got a new one... */
-
- theProperty = cmGetObjectProperty(theObject, propertyID);
- *newProperty = (CMBoolean)(theProperty == NULL);
-
- /* If we got a new property create it and init its fields... */
-
- if (*newProperty) { /* new property... */
- if ((theProperty = (TOCPropertyPtr)CMmalloc(sizeof(TOCProperty))) == NULL) {
- Container_Disable(container);
- ERROR1(CM_err_NoPropEntry, CONTAINERNAME);
- return (NULL);
- }
- cmNullListLinks(theProperty);
- cmInitList(&theProperty->valueHdrList);
- theProperty->propertyID = propertyID;
- theProperty->theObject = theObject; /* note, ptr to owning object */
- }
-
- /* To make it easier to read this mess we define the value and its header separately! */
-
- *theValueHdr = defineValue(container, theProperty, typeID, value, generation, flags, &newValueHdr);
-
- if (*theValueHdr == NULL) {
- if (*newProperty) CMfree(theProperty);
- return (NULL);
- }
-
- /* If we don't have a continued value, or even if we do, but its the first value of */
- /* the continuation, add it to property's value list. */
-
- if (newValueHdr) /* if new value hdr... */
- cmAppendListCell(&theProperty->valueHdrList, *theValueHdr); /* ...add to end of list*/
-
- return (theProperty); /* give caller the property ptr */
- }
-
-
- /*------------------------------------------------------------*
- | cmDefineObject - define a object with a property and value |
- *------------------------------------------------------------*
-
- This routine is called to define a new TOC entry for an object. We may have either a
- new object (id) or a preexisting one for which a new property and value are to be
- defined. All the fields for a TOC entry are passed. The objectFlags indicate the
- type of the object (more about this later).
-
- Note, the value associated with the property is appended to the the end of the
- property's value list. The caller has the option of getting the pointer to the value
- header (theValueHdr) when theValueHdr is not passed as NULL.
-
- The function returns a pointer to the object if it was successfully created and NULL if
- it wasn't. An error is reported if NULL is returned. Note, while error calls are not
- supposed to return we assume here they due just to be safe!
-
- The objectFlags determine how we treat the object and all the object fields (the other
- parameters). There are four possible objectFlags:
-
- (1). UndefinedObject Set if the object is to be created, but we don't yet know what
- its TOC entries are. Basically a null object (or placeholder)
- is created. It is an incomplete object in that there are no
- properties or types chained to the object (yet). This flag may
- be used in combination with the others if we know that the
- object ID corresponds to a property, type, or neither.
-
- (2). ObjectObject This flags is used when we don't know the type of the object but
- we know a property and type for it. If the object already exists
- and it's undefined (UndefinedObject) it is now considered as
- defined. If it was an undefined property or type, it now becomes
- a defined property or type. Of course, duplicate definitions
- are an error.
-
- (3). PropertyObject This is similar to ObjectObject, but here we know the object is
- for a property. It has to either not exist previously, or was
- previously flagged as UndefinedObject and PropertyObject.
-
- (4). TypeObject Same as PropertyObject, but for type objects.
-
- This routine is the "main control" of Container Manager object creation. It is used
- for "normal" object creation and while loading in a TOC from a preexisting container
- (i.e., from cmReadTOC()). After cmReadTOC(), or if we are writing and never do a
- cmReadTOC() we have the "normal" case. "We control the horizontal. Do not adjust your
- TV set". We always know what we are creating (yeah, I got a bridge to sell you too).
- Thus, in that case, we can link the objects to their master lists.
-
- For cmReadTOC() however, we are creating objects as we see thier id's in each TOC entry as
- we read them. We can have forward references to undefined and yet to be fully
- created objects and backward references to existing objects. We can also have multiple
- properties and values for the same object. Hence the flags and all they imply. We also
- do not link the objects. That is delayed until the TOC is completely read in. At that
- point the entire TOC is walked sequentially and the links built. We can also do some
- additional validation to make sure that everything is defined after the read in.
- */
-
- TOCObjectPtr cmDefineObject(ContainerPtr container, CM_ULONG objectID,
- const CM_ULONG propertyID, const CM_ULONG typeID,
- TOCValueBytesPtr value, const CM_ULONG generation,
- const CM_USHORT flags, const CM_USHORT objectFlags,
- TOCValueHdrPtr *theValueHdr)
- {
- TOCObjectPtr theObject;
- TOCPropertyPtr theProperty;
- TOCValueHdrPtr dummyValueHdr;
- CMBoolean dup, newProperty;
- CM_CHAR idStr[15];
-
- /* If the object ID's wrap, the objectID value will go to 0x00000000. We set it to */
- /* this when we bump the ID counter and detect we will wrap if we should ever attempt */
- /* to use another ID. But we may not. So the error is not reported when the wrap is */
- /* detected. The counter is only set to 0x00000000. That ID will work its way into */
- /* here to define another object. Now we're attempting to use the ID. So now is the */
- /* time to report the error. */
-
- if (objectID == 0x00000000UL) {
- Container_Disable(container);
- ERROR1(CM_err_wrappedIDs, CONTAINERNAME);
- return (NULL);
- }
-
- /* Create the basic object. If it already exists dup will be true If it doesn't, it */
- /* will be created.. Its fields will be initialized and the objectFlags set. */
-
- theObject = cmCreateObject(container->toc, (CMObjectID)objectID, objectFlags, &dup);
-
- if (theObject == NULL) {
- Container_Disable(container);
- ERROR1(CM_err_NoObjEntry, CONTAINERNAME);
- return (NULL);
- }
-
- /* If the object is to be an undefined place holder we ignore all the TOC field */
- /* parameters. But we must do a set of consistency checks before we will accept it. */
- /* Here are the conditions which make an undefined object acceptable: */
-
- /* (1). The object is new (not a dup). This is the first time we have seen it. */
-
- /* (2). The object has been seen before and it is still undefined. Thus we are */
- /* only trying to create the object because it was referenced more than once */
- /* as we were loading a TOC. */
-
- /* (3). The object has been seen before and IS defined. Its type is the same as */
- /* what we are trying make a place holder for. This occurs when the caller */
- /* thinks the id may be undefined but knows it's a property or type. If we */
- /* already defined it it better be the same (i.e., property or type). */
-
- /* (4). The object has been seen before and IS defined. But it was flagged as an */
- /* ObjectObject because we didn't know whether it was a property or type. The */
- /* caller still thinks it may be undefined, but knows it's a property or type.*/
- /* We thus CHANGE the ObjectObject flag to property or type now that we know */
- /* what it realy is. The end result is that a earlier defined but unknown kind*/
- /* of object (hence ObjectObject) now has a know kind, i.e., it's a property */
- /* or type. We have resolve a forward reference to an object. Note that if */
- /* the forward reference was to a known kind (property or type) and the knew */
- /* kind is different, we have an error. It is an attempt to multiply define */
- /* the object as both a property and a type. */
-
- /* In summary, whew... */
-
- if (objectFlags & UndefinedObject) { /* do what it says above! */
- if (!dup) return (theObject); /* (1) */
- if (theObject->objectFlags & UndefinedObject) return (theObject); /* (2) */
- if (theObject->objectFlags & objectFlags) return (theObject); /* (3) */
- if (theObject->objectFlags & (PropertyObject | TypeObject)) { /* (4) */
- Container_Disable(container);
- ERROR2(CM_err_MultDef, cmltostr(objectID, 1, false, idStr), CONTAINERNAME);
- return (NULL);
- }
- theObject->objectFlags = (CM_USHORT)(objectFlags & ~UndefinedObject);
- return (theObject); /* give caller back the object */
- }
-
- /* At this point we are trying to fully create an object. We know what it is because */
- /* the objectFlags didn't say UndefinedObject. If the object already exists, but it */
- /* is still undefined, we now mark it defined. */
-
- theObject->objectFlags &= ~UndefinedObject;
-
- /* If we get this far we are going to create a new property for an object or use an */
- /* existing one if one for the same property ID already exists. Either way the value */
- /* will be added to the chain off the property. The value will be appened to the end */
- /* of the chain. Here it goes... */
-
- if (theValueHdr == NULL) theValueHdr = &dummyValueHdr; /* supply dummy if necessary */
-
- theProperty = defineProperty(container, theObject, propertyID, typeID, value, generation,
- flags, &newProperty, theValueHdr);
- if (theProperty == NULL) return (NULL);
-
- /* If we indeed did create a new property add it to the object's property chain... */
-
- if (newProperty) cmAppendListCell(&theObject->propertyList, theProperty);
-
- /* Finally, if we are working with a "valid" TOC (remember "we control the */
- /* horizontal") then link the object to the master lists. */
-
- if (container->tocFullyReadIn) cmLinkObject(container->toc, theObject, NULL);
-
- return (theObject); /* give object and valueHdr to caller */
- }
-
-
- /*-----------------------------------------------------------------------------*
- | cmGetObjectProperty - find the property with the specified ID for an object |
- *-----------------------------------------------------------------------------*
-
- This routine takes a pointer to an object and scans its properties for the specified ID.
- The pointer to the property on the object's property chain is returned if found. NULL is
- returned if not found.
- */
-
- TOCPropertyPtr cmGetObjectProperty(TOCObjectPtr theObject, CM_ULONG propertyID)
- {
- TOCPropertyPtr theProperty;
-
- theProperty = (TOCPropertyPtr)cmGetListHead(&theObject->propertyList);
-
- while (theProperty) { /* scan each property on list */
- if (theProperty->propertyID == propertyID) break; /* exit as soon as we find ID */
- theProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
- }
-
- return (theProperty); /* return property ptr or NULL */
- }
-
-
- /*---------------------------------------------------------------------------------------*
- | cmGetPropertyType - find the value (header) with the specified type ID for a property |
- *---------------------------------------------------------------------------------------*
-
- This routine takes a pointer to a object's property and scans its value headers for one
- containing the specified type ID. The pointer to the value header is returned if found.
- NULL is returned if not found.
- */
-
- TOCValueHdrPtr cmGetPropertyType(TOCPropertyPtr theProperty, CM_ULONG typeID)
- {
- TOCValueHdrPtr theValueHdr;
-
- theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
-
- while (theValueHdr) { /* scan each value hdr on list */
- if (theValueHdr->typeID == typeID) break; /* exit as soon as we find ID */
- theValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
- }
-
- return (theValueHdr); /* return value hdr ptr or NULL */
- }
-
-
- /*----------------------------------------------------------------------------*
- | cmGet1ValueSize - return the size of the data for a single value (segment) |
- *----------------------------------------------------------------------------*
-
- This routine takes a pointer to a value (NOT a value header -- continued values are not
- worried about here) and returns the size represented by that value. For non-immediate
- values we simply return the length field from the value. Immediate value sizes are
- dependent on their type.
- */
-
- CM_ULONG cmGet1ValueSize(TOCValuePtr theValue)
- {
- CM_ULONG size;
-
- if (theValue->flags & kCMGlobalName)
- size = GetGlobalNameLength(theValue->value.globalName.globalNameSymbol) + 1;
- else if (theValue->flags & kCMImmediate)
- size = theValue->value.notImm.valueLen; /* immediate byte, word, or long data */
- else
- size = theValue->value.notImm.valueLen; /* non-immediate data */
-
- return (size);
- }
-
-
- #if CMVALIDATE
- /*-------------------------------------------------------------------------------*
- | cmValidateCMValue - validate a CMValue "refNum" passed to the API by the user |
- *-------------------------------------------------------------------------------*
-
- CMValue "refNum"s in the API are implemented in the Container Manager as pointers to
- TOCValueHdr structs. This routine is used to filter "bad" value "refNum"s passed to the
- higher level API interface ("CM...") routines. False is returned if the value is
- considered as "bad" and true otherwise.
-
- As currently implemented we return false but do NOT report an error for NULL pointers.
- That is because we return NULL from everthing we error out of earlier. The NULL test is
- just protection from letting such pointer from ever being used.
-
- Another check we do without reporting an error is on the container pointer. The refNums
- have a pointer to their container. The container has a special pointer pointing to
- itself. We check agains this pointer. If we think we don't have a valid container
- pointer then we assume we have a bad refNum and return false. No error can be reported
- because, wiut a container pointer, there is no way to do it!
-
- We DO report an error for attempting to use a deleted value. Deleted values are NOT
- freed. Rather they (actually their value headers) are put on a list of deleted values
- and flagged as deleted just for the purpose of testing here. If we had freed them, then
- we would be getting pointers to "garbage". By keeping them around we have valid pointers
- and we can do the test. Yuk!
- */
-
- CMBoolean cmValidateCMValue(CMValue value)
- {
- ContainerPtr container;
-
- if (value == NULL) return (false); /* false if bad value */
-
- container = ((TOCValueHdrPtr)value)->container; /* get container from refNum */
-
- NOPifNotInitialized(false); /* false if not initialized (NULL) */
-
- if (container != container->thisContainer) /* check against the check ptr */
- return (false);
-
- container = container->updatingContainer; /* use updating container */
-
- if (!VALIDATE) return (true); /* dynamically suppressed checking */
-
- /* Make sure value has not been deleted... */
-
- if ((((TOCValueHdrPtr)value)->valueFlags & ValueDeleted) != 0) {
- Container_Disable(container);
- ERROR1(CM_err_BadValue, CONTAINERNAME);
- return (false);
- }
-
- return (true); /* ok, it's good -- maybe! */
- }
- #endif
-
-
- #if CMVALIDATE
- /*---------------------------------------------------------------------------------*
- | cmValidateCMObject - validate a CMObject "refNum" passed to the API by the user |
- *---------------------------------------------------------------------------------*
-
- CMObject (and CMProperty, and CMType) "refNum"s in the API are implemented in the
- Container Manager as pointers to TOCObject structs. This routine is used to filter "bad"
- object "refNum"s passed to the higher level API interface ("CM...") routines. False is
- returned if the object is considered as "bad" and true otherwise.
-
- As currently implemented we return false but do NOT report an error for NULL pointers.
- That is because we return NULL from everthing we error out of earlier. The NULL test is
- just protection from letting such pointer from ever being used.
-
- Another check we do without reporting an error is on the container pointer. The refNums
- have a pointer to their container. The container has a special pointer pointing to
- itself. We check agains this pointer. If we think we don't have a valid container
- pointer then we assume we have a bad refNum and return false. No error can be reported
- because, wiut a container pointer, there is no way to do it!
-
- We DO report an error for attempting to use a deleted object. Deleted objects are NOT
- freed. Rather they are put on a list of deleted objects and flagged as deleted just for
- the purpose of testing here. If we had freed them, then we would be getting pointers to
- "garbage". By keeping them around we have valid pointers and we can do the test.
-
- For types and properties we also validate that such "refNum"s are indeed types or
- properties. The desired type is passed in the objectFlags. This test can always be
- forced to pass by passing 0xFFFFU. For types and properties, TypeObject or
- PropertyObject should be passed as appropriate.
- */
-
- CMBoolean cmValidateCMObjects(CMObject object, CM_USHORT objectFlags)
- {
- ContainerPtr container;
-
- if (object == NULL) return (false); /* false if bad value */
-
- container = ((TOCObjectPtr)object)->container; /* get container from refNum */
-
- NOPifNotInitialized(false); /* false if not initialized (NULL) */
-
- if (container != container->thisContainer) /* check against the check ptr */
- return (false);
-
- container = container->updatingContainer; /* use updating container */
-
- if (!VALIDATE) return (true); /* dynamically suppressed checking */
-
- /* Note, we assume that if an object is not NULL, it does indeed point to a container */
- /* object. We must make that assumption because it's the only way we can get the */
- /* container pointer. We return NULLs from everwhere when something goes wrong, so */
- /* the probability that a non-NULL is an object is pretty high. Besides, if the user */
- /* follos the rules, the second we report an error, the error report should abort */
- /* execution! */
-
- /* Make sure object has not been deleted... */
-
- if ((((TOCObjectPtr)object)->objectFlags & DeletedObject) != 0) {
- Container_Disable(container);
- ERROR1(CM_err_BadObject, CONTAINERNAME);
- return (false);
- }
-
- /* Make sure the kind of object is what is desired... */
-
- if ((((TOCObjectPtr)object)->objectFlags & objectFlags) == 0) {
- Container_Disable(container);
- ERROR2(CM_err_BadObjectKind, (objectFlags & TypeObject) ? "type" : "property", CONTAINERNAME);
- return (false);
- }
-
- return (true); /* ok, it's good -- maybe! */
- }
- #endif
-
-
- /*----------------------------------------------------------*
- | walkObject - walk all the structures for a single object |
- *----------------------------------------------------------*
-
- This routine serves two purposes. First it is an action routine for the object interator,
- cmForEachObject(). It is set by cmWalkThroughEntireTOC() to walk all the structures for a
- single object. Thus the net result from cmWalkThroughEntireTOC()'s point of view is that
- each structure in the the TOC is visited. Each object (by ascending order of object id),
- each property for each object, and each value (header and value) for each property.
-
- The second purpose of this routine is to act as the "guts" of cmWalkObject(). There are
- possible situations where we only want to walk a single object. Since that's what this
- routine does, cmWalkObject() calls it explicitly instead of repeatedly from
- cmForEachObject(). The result is the same. One object is walk per call.
-
- At each point in the data structures linked to an object we call a action routine
- specific to that structure. There is a object action routine (objectAction), a property
- action routine (propertyAction) and a value action routine (valueAction). See
- cmWalkThroughEntireTOC() or cmWalkObject() for further details on these routines. Note
- that if a routine pointer is NULL, no action is taken for that structure. There are also
- conventions for skipping the walking of parts of the data structures. Again, see
- cmWalkThroughEntireTOC() for details.
-
- The refcon in this context is the container pointer. It, in turn, contains a pointer
- back to a structure defined locally in cmWalkThroughEntireTOC() or cmWalkObject()
- containing all the action routine pointers and the original caller's refCon. See
- cmWalkThroughEntireTOC() or cmWalkObject() for details.
-
- Note, this "static" is intentionally left to default memory model under DOS since it is
- passed as a function pointer to cmForEachObject().
- */
-
- static void walkObject(TOCObjectPtr theObject, CMRefCon refCon)
- {
- ContainerPtr container = (ContainerPtr)refCon;
- TOCActionsPtr tocActions = (TOCActionsPtr)container->tocActions;
- TOCPropertyPtr theProperty, nextProperty;
- TOCValueHdrPtr theValueHdr, nextValueHdr;
- TOCValuePtr theValue, nextValue;
- TOCWalkReturns action;
-
- /* From this point on, the refCon to the action routines will be whatever the caller */
- /* supplied to cmWalkThroughEntireTOC(). We overload our used of "refCon" since */
- /* there's no sense creating another variable. */
-
- refCon = tocActions->refCon;
-
- /* Call the action routine to handle object structures... */
-
- if (tocActions->objectAction)
- if ((*tocActions->objectAction)(container, theObject, refCon) == WalkNextTOCObject)
- return;
-
- /* If there are no property, valueHdr, or value actions, there is nothing more to do..*/
-
- if (tocActions->propertyAction == NULL &&
- tocActions->valueHdrAction == NULL &&
- tocActions->valueAction == NULL)
- return;
-
- /* Walk through the property list for this object, calling its action routine... */
-
- theProperty = (TOCPropertyPtr)cmGetListHead(&theObject->propertyList);
- while (theProperty) {
- nextProperty = (TOCPropertyPtr)cmGetNextListCell(theProperty);
-
- if (tocActions->propertyAction) {
- action = (*tocActions->propertyAction)(container, theProperty, refCon);
- if (action == WalkNextTOCProperty) goto nextproperty;
- if (action == WalkNextTOCObject) return;
- }
-
- /* Walk through the value header list for each property, calling the action routine */
- /* for them. Note the value header pointer is passed since a single value is */
- /* considered as all the value entries chained off a single value header. That */
- /* only comes about from continued values. */
-
- if (tocActions->valueHdrAction || tocActions->valueAction) {
- theValueHdr = (TOCValueHdrPtr)cmGetListHead(&theProperty->valueHdrList);
- while (theValueHdr) {
- nextValueHdr = (TOCValueHdrPtr)cmGetNextListCell(theValueHdr);
-
- if (tocActions->valueHdrAction) {
- action = (*tocActions->valueHdrAction)(container, theValueHdr, refCon);
- if (action == WalkNextTOCValueHdr) goto nextvaluehdr;
- if (action == WalkNextTOCProperty) goto nextproperty;
- if (action == WalkNextTOCObject) return;
- }
-
- if (tocActions->valueAction) {
- theValue = (TOCValuePtr)cmGetListHead(&theValueHdr->valueList);
- while (theValue) {
- nextValue = (TOCValuePtr)cmGetNextListCell(theValue);
- action = (*tocActions->valueAction)(container, theValue, refCon);
- theValue = nextValue;
- if (action == WalkNextTOCValueHdr) goto nextvaluehdr;
- if (action == WalkNextTOCProperty) goto nextproperty;
- if (action == WalkNextTOCObject) return;
- } /* value */
- }
-
- nextvaluehdr:
- theValueHdr = nextValueHdr;
- } /* valueHdr */
- }
-
- nextproperty:
- theProperty = nextProperty;
- } /* property */
- }
-
-
- /*-------------------------------------------------------------------*
- | cmWalkThroughEntireTOC - apply actions to each structure in a TOC |
- *-------------------------------------------------------------------*
-
- This routine is called to walk the entire specified TOC by ascending object ID, starting
- with object ID's greater than or equal to the startingID and less than or equal to
- endingID, and to call an action routine appropriate for each kind of structure: object,
- property, value header, and value.
-
- Pointers to the four action routines are passed with headers as shown in the definition.
- If a pointer is passed as NULL, no action will be performed for the corresponding
- structure. Each function has return codes (type TOCWalkReturns) that direct the walking
- through the TOC. The action routines should return the codes shown in the following
- table:
-
- Walk... NextTOCObject | NextTOCProperty | NextTOCValueHdr | NextTOCValue
- ===============|===============|=================|=================|==============
- objectAction | next object | next property* | |
- propertyAction | next object | next property | next value hdr* |
- valueHdrAction | next object | next property | next value hdr | next segment*
- valueAction | next object | next property | next value hdr | next segment*
- ---------------+---------------+-----------------+-----------------+--------------
-
- The entries marked with "*" are the returns that should be used in the normal case to
- fully walk each data structure of the TOC. Those not marked with "*" are for special
- cases to abort the walk to go to the next indicated data structure. Walks are sequential
- through the TOC by ascending object ID. Thus skipping a particular walk of one structure
- only makes sense if the skip is possible. That is the reason not all returns are allowed
- in all action routines. In reality, only the non-starred returns are checked on return
- from an action routine. So the default is the starred return (i.e., keep walking normally)
- if a non-starred is not returned.
-
- A "refCon" is also passed which the caller can use as a communication facility to convey
- additional info to the action routines.
-
- This function returns 0 to indicate successful completion, and non-zero otherwise. Thus
- if AbortWalkThroughEntireTOC(x) is used to abort processing, the x should be a positive
- non-zero integer.
-
- Note, if the property, value header, and value action routines are supplied as NULL,
- then cmWalkThroughEntireTOC() is functionally equivalent to cmForEachObject() which just
- applies a single action routine to the objects. Thus if only the objects are to be
- walked, it is recommended that cmForEachObject() be used instead of
- cmWalkThroughEntireTOC().
-
- Note also, this routine uses cmForEachObject() to walk the TOC objects with walkObject()
- defined above as the action routine. It handles the walking of the structures "below"
- the object level. We use the refCon parameter to cmForEachObject() to communicate to
- walkObject() all the action routine pointers and the caller's refCon. In particular,
- the refCon is a pointer to the container. In the container is a pointer BACK to a local
- data structure we set up here with all the info.
-
- We can get away with this stunt because this routine is always up level in the stack and
- its locals are valid while cmForEachObject() is doing its thing.
-
- The additional trick we pull here is to set up a setjmp/longjmp environment vector for
- the AbortWalkThroughEntireTOC() macro. That macro is a longjmp back to here with a
- value we return as the function result. Since the data structure is local here, the only
- way the macro can get at the environment vector is through the pointer in the container.
- I don't want the local data structure visable outside this file. So the only place the
- vector can be placed is at the START of the data structure. Win a few, loose a few!
- */
-
- int cmWalkThroughEntireTOC(ContainerPtr container, void *toc,
- CMObjectID startingID, CMObjectID endingID,
- CMRefCon refCon,
- TOCWalkReturns (*objectAction)(ContainerPtr container, TOCObjectPtr theObject, CMRefCon refCon),
- TOCWalkReturns (*propertyAction)(ContainerPtr container, TOCPropertyPtr theProperty, CMRefCon refCon),
- TOCWalkReturns (*valueHdrAction)(ContainerPtr container, TOCValueHdrPtr theValueHdr, CMRefCon refCon),
- TOCWalkReturns (*valueAction)(ContainerPtr container, TOCValuePtr theValue, CMRefCon refCon))
- {
- TOCActions tocActions;
-
- tocActions.objectAction = objectAction; /* save the action routine ptrs */
- tocActions.propertyAction = propertyAction;
- tocActions.valueHdrAction = valueHdrAction;
- tocActions.valueAction = valueAction;
-
- tocActions.refCon = refCon; /* save caller's refCon */
-
- if (setjmp(tocActions.walkThroughEnv)) /* if longjmp taken... */
- return (1); /* ...report the bad news */
-
- container->tocActions = (void *)&tocActions; /* set ptr to our info in container */
-
- return (cmForEachObject(toc, startingID, endingID, (void *)container, walkObject));
- }
-
-
- /*-----------------------------------------------------------------------*
- | cmWalkObject - apply actions to each structure of a single TOC object |
- *-----------------------------------------------------------------------*
-
- This routine is called to walk a single object (theObject) and call an action routine
- appropriate for each kind of structure: object, property, value header, and value.
- Pointers to the four action routines are passed with headers as shown in the definition.
- If a pointer is passed as NULL, no action will be performed for the corresponding
- structure. The returns are the same as cmWalkThroughEntireTOC(). See it for further
- details.
-
- A "refCon" is also passed which the caller can use as a communication facility
- to convey additional info to the action routines. 0 is returned to indicate successfull
- completion. Use the AbortWalkObject(x) (a macro) in an action routine to abort the
- walk. The "x" is a integer which is returned from cmWalkObject() so it should not be 0.
-
- Implementation note: This routine is basically "glue" code for outside callers to
- walkObject(). walkObject() is defined as an action routine for cmForEachObject() which is
- called by cmWalkThroughEntireTOC() above. The walkObject() routine wals a single object
- and applies the action routines at each point in an objects data structure. This is
- exactly what is needed here so that why we use. There's no sence reinventing the wheel!
-
- Of course, since walkObject() doesn't know we are calling it from here, and things it's an
- action routine for cmForEachObject(), which was called by cmWalkThroughEntireTOC(), then
- we must observe ALL the conventions set up by cmWalkThroughEntireTOC(). See comments
- for cmWalkThroughEntireTOC() for these conventsions (the refCon, aborting, etc.).
-
- The only thing different in the code below is that we explicitly call walkObject() instead
- of cmForEachObject() and a more apporpriate "abort" macro is provided. The macro is
- called AbortWalkObject(). But expands to identically to what AbortWalkThroughEntireTOC()
- expands to.
- */
-
- int cmWalkObject(ContainerPtr container, TOCObjectPtr theObject, CMRefCon refCon,
- TOCWalkReturns (*objectAction)(ContainerPtr container, TOCObjectPtr theObject, CMRefCon refCon),
- TOCWalkReturns (*propertyAction)(ContainerPtr container, TOCPropertyPtr theProperty, CMRefCon refCon),
- TOCWalkReturns (*valueHdrAction)(ContainerPtr container, TOCValueHdrPtr theValueHdr, CMRefCon refCon),
- TOCWalkReturns (*valueAction)(ContainerPtr container, TOCValuePtr theValue, CMRefCon refCon))
- {
- TOCActions tocActions;
-
- tocActions.objectAction = objectAction; /* save the action routine ptrs */
- tocActions.propertyAction = propertyAction;
- tocActions.valueHdrAction = valueHdrAction;
- tocActions.valueAction = valueAction;
-
- tocActions.refCon = refCon; /* save caller's refCon */
-
- if (setjmp(tocActions.walkThroughEnv)) /* if longjmp taken... */
- return (1); /* ...report the bad news */
-
- container->tocActions = (void *)&tocActions; /* set ptr to our info in container */
-
- walkObject(theObject, (void *)container); /* walk single object (not a beast) */
-
- return (0); /* what do you know, we made it! */
- }
-
- CM_END_CFUNCTIONS
-